#include "../../Include/System/WizDLL.hpp"
#include "../../Include/Base/WizBase.hpp"

namespace Wiz
{
    namespace DLL
    {
        //////////////////////////////////////////////////////////////////////////
        typedef ::Wiz::DLL::Type::tHandler Handler;

        WIZ_PRIVATE ::Wiz::DLL::Handler Open(::Wiz::String::In InPath);
        WIZ_PRIVATE ::Wiz::DLL::Handler Open(::Wiz::WString::In InPath);

        WIZ_PRIVATE ::Wiz::Void::Ptr Export(const ::Wiz::DLL::Handler&, ::Wiz::String::In InName);
        WIZ_PRIVATE ::Wiz::Void::Ptr Export(const ::Wiz::DLL::Handler&, ::Wiz::WString::In InName);

        WIZ_PRIVATE ::Wiz::Void::Type Close(::Wiz::DLL::Handler&);
        //////////////////////////////////////////////////////////////////////////

        Type::Type(::Wiz::String::In InPath)
        {
            this->Open(InPath);
        }
        Type::Type(::Wiz::WString::In InPath)
        {
            this->Open(InPath);
        }

        Type::~Type()
        {
            this->Close();
        }

        ::Wiz::Bool::Type Type::Open(::Wiz::String::In InPath)
        {
            /// Opened
            if (::Wiz::IsValidPtr(this->m_Core))
            {
                return ::Wiz::Bool::False;
            }

            this->m_Core = ::Wiz::DLL::Open(InPath);

            if (this->NotValid())
            {
                ::Wiz::Exception::Throw(::Wiz::Exception::LoadDllFailed());
            }

            return ::Wiz::IsValidPtr(this->m_Core);
        }
        ::Wiz::Bool::Type Type::Open(::Wiz::WString::In InPath)
        {
            /// Opened
            if (::Wiz::IsValidPtr(this->m_Core))
            {
                return ::Wiz::Bool::False;
            }

            this->m_Core = ::Wiz::DLL::Open(InPath);

            if (this->NotValid())
            {
                ::Wiz::Exception::Throw(::Wiz::Exception::LoadDllFailed());
            }

            return ::Wiz::IsValidPtr(this->m_Core);
        }

        ::Wiz::Void::Type Type::Close()
        {
            if (::Wiz::IsValidPtr(this->m_Core))
            {
                ::Wiz::DLL::Close(this->m_Core);
                this->m_Core = WIZ_NULLPTR;
            }
        }

        ::Wiz::Bool::Type Type::IsValid() const
        {
            return ::Wiz::IsValidPtr(this->m_Core);
        }
        ::Wiz::Bool::Type Type::NotValid() const
        {
            return ::Wiz::NotValidPtr(this->m_Core);
        }

        ::Wiz::Void::Ptr Type::DoExport(::Wiz::String::In InName) const
        {
            return ::Wiz::DLL::Export(this->m_Core, InName);
        }
        ::Wiz::Void::Ptr Type::DoExport(::Wiz::WString::In InName) const
        {
            return ::Wiz::DLL::Export(this->m_Core, InName);
        }
        //////////////////////////////////////////////////////////////////////////
        ::Wiz::DLL::Handler Open(::Wiz::String::In InPath)
        {
            if (InPath.Size() < 2)
            {
                return WIZ_NULLPTR;
            }
#if (WIZ_CFG_PLATFORM == WIZ_CFG_PLATFORM_WINDOWS)
            return ::Wiz::Cast::Reinterpret<::Wiz::DLL::Handler>(LoadLibraryA(InPath.GetCStr()));
#elif (WIZ_CFG_PLATFORM == WIZ_CFG_PLATFORM_LINUX)
            return ::Wiz::Cast::Reinterpret<::Wiz::DLL::Handler>(dlopen(InPath.GetCStr()));
#endif /*WIZ_CFG_PLATFORM*/

        }
        ::Wiz::DLL::Handler Open(::Wiz::WString::In InPath)
        {
            if (InPath.Size() < 2)
            {
                return WIZ_NULLPTR;
            }
#if (WIZ_CFG_PLATFORM == WIZ_CFG_PLATFORM_WINDOWS)
            return ::Wiz::Cast::Reinterpret<::Wiz::DLL::Handler>(LoadLibraryW(InPath.GetCStr()));
#elif (WIZ_CFG_PLATFORM == WIZ_CFG_PLATFORM_LINUX)
            ::Wiz::String::Type AnsiPath;
            ::Wiz::WCharStrToMultiBytesStr(AnsiPath, InPath, CP_ACP);
            return ::Wiz::Cast::Reinterpret<::Wiz::DLL::Handler>(dlopen(AnsiPath.GetCStr()));
#endif /*WIZ_CFG_PLATFORM*/
        }

        ::Wiz::Void::Ptr Export(const ::Wiz::DLL::Handler& InHandler, ::Wiz::String::In InName)
        {
            if (InName.Size() < 2)
            {
                return WIZ_NULLPTR;
            }
#if (WIZ_CFG_PLATFORM == WIZ_CFG_PLATFORM_WINDOWS)
            return ::GetProcAddress(::Wiz::Cast::Reinterpret<HMODULE>(InHandler), InName.GetCStr());
#elif (WIZ_CFG_PLATFORM == WIZ_CFG_PLATFORM_LINUX)
            return dlsym(InHandler, InName.GetCStr());
#endif /*WIZ_CFG_PLATFORM*/
        }
        ::Wiz::Void::Ptr Export(const ::Wiz::DLL::Handler& InHandler, ::Wiz::WString::In InName)
        {
            if (InName.Size() < 2)
            {
                return WIZ_NULLPTR;
            }
            ::Wiz::String::Type AnsiPath;
            ::Wiz::WCharStrToMultiBytesStr(AnsiPath, InName, CP_ACP);
#if (WIZ_CFG_PLATFORM == WIZ_CFG_PLATFORM_WINDOWS)
            return ::GetProcAddress(::Wiz::Cast::Reinterpret<HMODULE>(InHandler), AnsiPath.GetCStr());
#elif (WIZ_CFG_PLATFORM == WIZ_CFG_PLATFORM_LINUX)
            return dlsym(InHandler, AnsiPath.GetCStr());
#endif /*WIZ_CFG_PLATFORM*/
        }

        ::Wiz::Void::Type Close(::Wiz::DLL::Handler& InHandler)
        {
#if (WIZ_CFG_PLATFORM == WIZ_CFG_PLATFORM_WINDOWS)
            FreeLibrary(::Wiz::Cast::Reinterpret<HMODULE>(InHandler));
#elif (WIZ_CFG_PLATFORM == WIZ_CFG_PLATFORM_LINUX)
            dlclose(InHandler);
#endif /*WIZ_CFG_PLATFORM*/
        }
        //////////////////////////////////////////////////////////////////////////
    } /// end of namespace DLL
} /// end of namespace Wiz

